package com.caifeng.response;

import com.caifeng.domain.ResponseCode;
import com.caifeng.domain.ResponseResult;
import com.caifeng.domain.ServiceException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Path;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.List;
import java.util.stream.StreamSupport;

@Slf4j
@RestControllerAdvice(annotations = MiaoResponse.class)
public class ResponseAdvice implements ResponseBodyAdvice<Object> {


    @Override
    public boolean supports(@NonNull MethodParameter returnType, @NonNull Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(
            Object data,
            @NonNull MethodParameter returnType,
            @NonNull MediaType selectedContentType,
            @NonNull Class selectedConverterType,
            @NonNull ServerHttpRequest request,
            @NonNull ServerHttpResponse response
    ) {
        if (MediaType.APPLICATION_JSON.equals(selectedContentType)) {
            if (data instanceof ResponseResult) {
                return data;
            } else {
                return new ResponseResult(ResponseCode.SUCCESS, data);
            }
        }

        return new ResponseResult(ResponseCode.RESPONSE_FORMAT_ERROR);
    }

    /**
     * 处理未捕获的RuntimeException
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseResult handleRuntimeException(RuntimeException e) {
        log.error("e: ", e);
        return new ResponseResult(ResponseCode.SERVICE_ERROR);
    }

    /**
     * 处理业务异常ServiceException
     */
    @ExceptionHandler(ServiceException.class)
    public ResponseResult handleBaseException(ServiceException be) {
        return new ResponseResult(be.getCode(), be.getDescription());
    }

    @ExceptionHandler(AuthorizationDeniedException.class)
    public ResponseResult handleException(AuthorizationDeniedException e) {
        return new ResponseResult(ResponseCode.INSUFFICIENT_PERMISSIONS);
    }
    @ExceptionHandler(value = {
            MethodArgumentNotValidException.class,
            BindException.class})
    public ResponseResult methodArgumentNotValidHandler(Exception e) {
        BindingResult bindingResult;
        if (e instanceof MethodArgumentNotValidException) {

            bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
        } else {
            bindingResult = ((BindException) e).getBindingResult();
        }
        FieldError fieldError = bindingResult.getFieldError();
        String data = "";
        if (fieldError != null) {
            data = "[" + fieldError.getField() + "]" +
                    fieldError.getDefaultMessage();
        }
        return new ResponseResult(ResponseCode.PARAM_ERROR, data);
    }

    @ExceptionHandler(value = {
            ConstraintViolationException.class,
            MissingServletRequestParameterException.class})
    public ResponseResult constraintViolationHandler(Exception e) {
        String field = "";
        String msg = "";
        if (e instanceof ConstraintViolationException) {
            ConstraintViolation<?> constraint = ((ConstraintViolationException) e)

                    .getConstraintViolations().stream().findFirst().orElse(null);
            if (constraint != null) {
                List<Path.Node> pathList = StreamSupport.stream
                                (constraint.getPropertyPath().spliterator(), false)
                        .toList();
                field = pathList.get(pathList.size() - 1).getName();
                msg = constraint.getMessage();
            }
        } else {
            field = ((MissingServletRequestParameterException) e).getParameterName();
            msg = "参数值缺失";
        }
        return new ResponseResult(ResponseCode.PARAM_ERROR, "[" + field + "]" + msg);
    }
}

